package geovista.jts.geom;

import com.vividsolutions.jts.geom.*;

/**
 * This class represents a affine transformation on the 2D Cartesian plane. It
 * can be used to transform a {@link Coordinate} or {@link Geometry}. An affine
 * transformation is a mapping of the 2D plane into itself via a series of
 * transformations of the following basic types:
 * <ul>
 * <li>reflection
 * <li>rotation
 * <li>scaling
 * <li>shearing
 * <li>translation
 * </ul>
 * In general, affine transformations preserve straightness and parallel lines,
 * but do not preserve distance or shape.
 * <p>
 * An affine transformation can be represented by a 3x3 matrix in the following
 * form: <blockquote>
 * 
 * <pre>
 * T = | m00 m01 m02 |
 *     | m10 m11 m12 |
 *     |  0   0   1  |
 * </pre>
 * 
 * </blockquote> A coordinate P = (x, y) can be transformed to a new coordinate
 * P' = (x', y') by representing it as a 3x1 matrix and using matrix
 * multiplication to compute: <blockquote>
 * 
 * <pre>
 * | x' |  = T x | x |
 * | y' |        | y |
 * | 1  |        | 1 |
 * </pre>
 * 
 * </blockquote> Affine transformations can be composed using the
 * {@link #compose} method. Composition is not commutative. Composition is
 * computed via multiplication of the transformation matrices as follows:
 * <blockquote>
 * 
 * <pre>
 * A.compose(B) = T&lt;sub&gt;B&lt;/sub&gt; x T&lt;sub&gt;A&lt;/sub&gt;
 * </pre>
 * 
 * </blockquote> This produces a transformation whose effect is that of A
 * followed by B. The methods {@link #reflect}, {@link #rotate},
 * {@link #scale}, {@link #shear}, and {@link #translate} have the effect of
 * composing a transformation of that type with the transformation they are
 * applied to.
 * 
 * @author Martin Davis
 * 
 */
public class AffineTransformation implements Cloneable, CoordinateFilter {

	/**
	 * Creates a transformation for a reflection about the line (x0,y0) -
	 * (x1,y1).
	 * 
	 * @param x0
	 *            the x-ordinate of a point on the reflection line
	 * @param y0
	 *            the y-ordinate of a point on the reflection line
	 * @param x1
	 *            the x-ordinate of a another point on the reflection line
	 * @param y1
	 *            the y-ordinate of a another point on the reflection line
	 * @return a transformation for the reflection
	 */
	public static AffineTransformation reflectionInstance(double x0, double y0,
			double x1, double y1) {
		AffineTransformation trans = new AffineTransformation();
		trans.setToReflection(x0, y0, x1, y1);
		return trans;
	}

	/**
	 * Creates a transformation for a reflection about the line (0,0) - (x,y).
	 * 
	 * @param x
	 *            the x-ordinate of a point on the reflection line
	 * @param y
	 *            the y-ordinate of a point on the reflection line
	 * @return a transformation for the reflection
	 */
	public static AffineTransformation reflectionInstance(double x, double y) {
		AffineTransformation trans = new AffineTransformation();
		trans.setToReflection(x, y);
		return trans;
	}

	/**
	 * Creates a transformation for a rotation about the origin by an angle
	 * <i>theta</i>. Positive angles correspond to a rotation in the
	 * counter-clockwise direction.
	 * 
	 * @param theta
	 *            the rotation angle, in radians
	 * @return a transformation for the rotation
	 */
	public static AffineTransformation rotationInstance(double theta) {
		return rotationInstance(Math.sin(theta), Math.cos(theta));
	}

	/**
	 * Creates a transformation for a rotation by an angle <i>theta</i>,
	 * specified by the sine and cosine of the angle. This allows providing
	 * exact values for sin(theta) and cos(theta) for the common case of
	 * rotations of multiples of quarter-circles.
	 * 
	 * @param sinTheta
	 *            the sine of the rotation angle
	 * @param cosTheta
	 *            the cosine of the rotation angle
	 * @return a transformation for the rotation
	 */
	public static AffineTransformation rotationInstance(double sinTheta,
			double cosTheta) {
		AffineTransformation trans = new AffineTransformation();
		trans.setToRotation(sinTheta, cosTheta);
		return trans;
	}

	public static AffineTransformation scaleInstance(double xScale,
			double yScale) {
		AffineTransformation trans = new AffineTransformation();
		trans.setToScale(xScale, yScale);
		return trans;
	}

	public static AffineTransformation shearInstance(double xShear,
			double yShear) {
		AffineTransformation trans = new AffineTransformation();
		trans.setToShear(xShear, yShear);
		return trans;
	}

	public static AffineTransformation translationInstance(double x, double y) {
		AffineTransformation trans = new AffineTransformation();
		trans.setToTranslation(x, y);
		return trans;
	}

	// affine matrix entries
	// (bottom row is always [ 0 0 1 ])
	private double m00;
	private double m01;
	private double m02;
	private double m10;
	private double m11;
	private double m12;

	/**
	 * Constructs a new identity transformation
	 * 
	 */
	public AffineTransformation() {
		setToIdentity();
	}

	/**
	 * Constructs a new transformation whose matrix has the specified values.
	 * 
	 * @param matrix
	 *            an array containing the 6 values { m00, m01, m02, m10, m11,
	 *            m12 }
	 * @throws NullPointerException
	 *             if matrix is null
	 * @throws ArrayIndexOutOfBoundsException
	 *             if matrix is too small
	 */
	public AffineTransformation(double[] matrix) {
		m00 = matrix[0];
		m01 = matrix[1];
		m02 = matrix[2];
		m10 = matrix[3];
		m11 = matrix[4];
		m12 = matrix[5];
	}

	/**
	 * Constructs a new transformation whose matrix has the specified values.
	 * 
	 * @param m00
	 *            the entry for the [0, 0] element in the transformation matrix
	 * @param m01
	 *            the entry for the [0, 1] element in the transformation matrix
	 * @param m02
	 *            the entry for the [0, 2] element in the transformation matrix
	 * @param m10
	 *            the entry for the [1, 0] element in the transformation matrix
	 * @param m11
	 *            the entry for the [1, 1] element in the transformation matrix
	 * @param m12
	 *            the entry for the [1, 2] element in the transformation matrix
	 */
	public AffineTransformation(double m00, double m01, double m02, double m10,
			double m11, double m12) {
		setTransformation(m00, m01, m02, m10, m11, m12);
	}

	/**
	 * Constructs a transformation which is a copy of the given one.
	 * 
	 * @param trans
	 *            the transformation to copy
	 */
	public AffineTransformation(AffineTransformation trans) {
		setTransformation(trans);
	}

	/**
	 * Constructs a transformation which maps the given source points into the
	 * given destination points.
	 * 
	 * @param src0
	 *            source point 0
	 * @param src1
	 *            source point 1
	 * @param src2
	 *            source point 2
	 * @param dest0
	 *            the mapped point for source point 0
	 * @param dest1
	 *            the mapped point for source point 1
	 * @param dest2
	 *            the mapped point for source point 2
	 * 
	 */
	public AffineTransformation(Coordinate src0, Coordinate src1,
			Coordinate src2, Coordinate dest0, Coordinate dest1,
			Coordinate dest2) {
	}

	/**
	 * Sets this transformation to be the identity transformation. The identity
	 * transformation has the matrix: <blockquote>
	 * 
	 * <pre>
	 * | 1 0 0 |
	 * | 0 1 0 |
	 * | 0 0 1 |
	 * </pre>
	 * 
	 * </blockquote>
	 * 
	 * @return this transformation, with an updated matrix
	 */
	public AffineTransformation setToIdentity() {
		m00 = 1.0;
		m01 = 0.0;
		m02 = 0.0;
		m10 = 0.0;
		m11 = 1.0;
		m12 = 0.0;
		return this;
	}

	/**
	 * Sets this transformation's matrix to have the given values.
	 * 
	 * @param m00
	 *            the entry for the [0, 0] element in the transformation matrix
	 * @param m01
	 *            the entry for the [0, 1] element in the transformation matrix
	 * @param m02
	 *            the entry for the [0, 2] element in the transformation matrix
	 * @param m10
	 *            the entry for the [1, 0] element in the transformation matrix
	 * @param m11
	 *            the entry for the [1, 1] element in the transformation matrix
	 * @param m12
	 *            the entry for the [1, 2] element in the transformation matrix
	 * @return this transformation, with an updated matrix
	 */
	public AffineTransformation setTransformation(double m00, double m01,
			double m02, double m10, double m11, double m12) {
		this.m00 = m00;
		this.m01 = m01;
		this.m02 = m02;
		this.m10 = m10;
		this.m11 = m11;
		this.m12 = m12;
		return this;
	}

	/**
	 * Sets this transformation to be a copy of the given one
	 * 
	 * @param trans
	 *            a transformation to copy
	 * @return this transformation, with an updated matrix
	 */
	public AffineTransformation setTransformation(AffineTransformation trans) {
		m00 = trans.m00;
		m01 = trans.m01;
		m02 = trans.m02;
		m10 = trans.m10;
		m11 = trans.m11;
		m12 = trans.m12;
		return this;
	}

	/**
	 * Gets an array containing the entries of the transformation matrix. Only
	 * the 6 non-trivial entries are returned, in the sequence:
	 * 
	 * <pre>
	 * m00, m01, m02, m10, m11, m12
	 * </pre>
	 * 
	 * @return an array of length 6
	 */
	public double[] getMatrixEntries() {
		return new double[] { m00, m01, m02, m10, m11, m12 };
	}

	/**
	 * Computes the determinant of the transformation matrix. The determinant is
	 * computed as: <blockquote>
	 * 
	 * <pre>
	 * | m00 m01 m02 |
	 * | m10 m11 m12 | = m00 * m11 - m01 * m10
	 * |  0   0   1  |
	 * </pre>
	 * 
	 * </blockquote> If the determinant is zero, the transform is singular (not
	 * invertible), and operations which attempt to compute an inverse will
	 * throw a <tt>NoninvertibleTransformException</tt>.
	 * 
	 * @return the determinant of the transformation
	 * @see #getInverse()
	 */
	public double getDeterminant() {
		return m00 * m11 - m01 * m10;
	}

	/**
	 * Computes the inverse of this transformation, if one exists. The inverse
	 * is the transformation which when composed with this one produces the
	 * identity transformation. A transformation has an inverse if and only if
	 * it is not singular (i.e. its determinant is non-zero). Geometrically, an
	 * transformation is non-invertible if it maps the plane to a line or a
	 * point. If no inverse exists this method will throw a
	 * <tt>NoninvertibleTransformationException</tt>.
	 * <p>
	 * The matrix of the inverse is equal to the inverse of the matrix for the
	 * transformation. It is computed as follows: <blockquote>
	 * 
	 * <pre>
	 *   
	 *                 1    
	 * inverse(A)  =  ---   x  adjoint(A) 
	 *                det 
	 * 
	 * 
	 *             =   1       |  m11  -m01   m01*m12-m02*m11  |
	 *                ---   x  | -m10   m00  -m00*m12+m10*m02  |
	 *                det      |  0     0     m00*m11-m10*m01  |
	 * 
	 * 
	 * 
	 *             = |  m11/det  -m01/det   m01*m12-m02*m11/det |
	 *               | -m10/det   m00/det  -m00*m12+m10*m02/det |
	 *               |   0           0          1               |
	 * 
	 * 
	 * </pre>
	 * 
	 * </blockquote>
	 * 
	 * @return a new inverse transformation
	 * @throws NoninvertibleTransformationException
	 * @see #getDeterminant()
	 */
	public AffineTransformation getInverse()
			throws NoninvertibleTransformationException {
		double det = getDeterminant();
		if (det == 0)
			throw new NoninvertibleTransformationException(
					"Transformation is non-invertible");

		double im00 = m11 / det;
		double im10 = -m10 / det;
		double im01 = -m01 / det;
		double im11 = m00 / det;
		double im02 = (m01 * m12 - m02 * m11) / det;
		double im12 = (-m00 * m12 + m10 * m02) / det;

		return new AffineTransformation(im00, im01, im02, im10, im11, im12);
	}

	/**
	 * Explicitly computes the math for a reflection. May not work.
	 * 
	 * @param x0
	 * @param y0
	 * @param x1
	 * @param y1
	 * @return
	 */
	public AffineTransformation setToReflectionBasic(double x0, double y0,
			double x1, double y1) {
		if (x0 == x1 && y0 == y1) {
			throw new IllegalArgumentException(
					"Reflection line points must be distinct");
		}
		double dx = x1 - x0;
		double dy = y1 - y0;
		double d = Math.sqrt(dx * dx + dy * dy);
		double sin = dy / d;
		double cos = dx / d;
		double cs2 = 2 * sin * cos;
		double c2s2 = cos * cos - sin * sin;
		m00 = c2s2;
		m01 = cs2;
		m02 = 0.0;
		m10 = cs2;
		m11 = -c2s2;
		m12 = 0.0;
		return this;
	}

	public AffineTransformation setToReflection(double x0, double y0,
			double x1, double y1) {
		if (x0 == x1 && y0 == y1) {
			throw new IllegalArgumentException(
					"Reflection line points must be distinct");
		}
		// translate line vector to origin
		setToTranslation(-x0, -y0);

		// rotate vector to positive x axis direction
		double dx = x1 - x0;
		double dy = y1 - y0;
		double d = Math.sqrt(dx * dx + dy * dy);
		double sin = dy / d;
		double cos = dx / d;
		rotate(-sin, cos);
		// reflect about the x axis
		scale(1, -1);
		// rotate back
		rotate(sin, cos);
		// translate back
		translate(x0, y0);
		return this;
	}

	/**
	 * Sets this transformation to be a reflection about the line defined by
	 * vector (x,y). The transformation for a reflection is computed by:
	 * <blockquote>
	 * 
	 * <pre>
	 * d = sqrt(x&lt;sup&gt;2&lt;/sup&gt; + y&lt;sup&gt;2&lt;/sup&gt;)  
	 * sin = x / d;
	 * cos = x / d;
	 * 
	 * T&lt;sub&gt;ref&lt;/sub&gt; = T&lt;sub&gt;rot(sin, cos)&lt;/sub&gt; x T&lt;sub&gt;scale(1, -1)&lt;/sub&gt; x T&lt;sub&gt;rot(-sin, cos)&lt;/sub  
	 * </pre>
	 * 
	 * </blockquote>
	 * 
	 * @param x
	 *            the x-component of the reflection line vector
	 * @param y
	 *            the y-component of the reflection line vector
	 * @return this transformation, with an updated matrix
	 */
	public AffineTransformation setToReflection(double x, double y) {
		if (x == 0.0 && y == 0.0) {
			throw new IllegalArgumentException(
					"Reflection vector must be non-zero");
		}
		// rotate vector to positive x axis direction
		double d = Math.sqrt(x * x + y * y);
		double sin = y / d;
		double cos = x / d;
		rotate(-sin, cos);
		// reflect about the x-axis
		scale(1, -1);
		// rotate back
		rotate(sin, cos);
		return this;
	}

	/**
	 * Sets this transformation to be a rotation. A positive rotation angle
	 * corresponds to a counter-clockwise rotation. The transformation matrix
	 * for a rotation by an angle <tt>theta</tt> has the value: <blockquote>
	 * 
	 * <pre>
	 *   
	 * |  cos(theta)  -sin(theta)   0 |
	 * |  sin(theta)   cos(theta)   0 |
	 * |           0            0   1 |
	 * </pre>
	 * 
	 * </blockquote>
	 * 
	 * @param theta
	 *            the rotation angle, in radians
	 * @return this transformation, with an updated matrix
	 */
	@SuppressWarnings("unused")
	private AffineTransformation setToRotation(double theta) {
		setToRotation(Math.sin(theta), Math.cos(theta));
		return this;
	}

	/**
	 * Sets this transformation to be a rotation by specifying the sin and cos
	 * of the rotation angle directly. The transformation matrix for the
	 * rotation has the value: <blockquote>
	 * 
	 * <pre>
	 *   
	 * |  cosTheta  -sinTheta   0 |
	 * |  sinTheta   cosTheta   0 |
	 * |         0          0   1 |
	 * </pre>
	 * 
	 * </blockquote>
	 * 
	 * @param sinTheta
	 *            the sine of the rotation angle
	 * @param cosTheta
	 *            the cosine of the rotation angle
	 * @return this transformation, with an updated matrix
	 */
	public AffineTransformation setToRotation(double sinTheta, double cosTheta) {
		m00 = cosTheta;
		m01 = -sinTheta;
		m02 = 0.0;
		m10 = sinTheta;
		m11 = cosTheta;
		m12 = 0.0;
		return this;
	}

	/**
	 * Sets this transformation to be a scaling. The transformation matrix for a
	 * scale has the value: <blockquote>
	 * 
	 * <pre>
	 *   
	 * |  xScale      0  dx |
	 * |  1      yScale  dy |
	 * |  0           0   1 |
	 * </pre>
	 * 
	 * </blockquote>
	 * 
	 * @param xScale
	 *            the amount to scale x-ordinates by
	 * @param yScale
	 *            the amount to scale y-ordinates by
	 * @return this transformation, with an updated matrix
	 */
	public AffineTransformation setToScale(double xScale, double yScale) {
		m00 = xScale;
		m01 = 0.0;
		m02 = 0.0;
		m10 = 0.0;
		m11 = yScale;
		m12 = 0.0;
		return this;
	}

	/**
	 * Sets this transformation to be a shear. The transformation matrix for a
	 * shear has the value: <blockquote>
	 * 
	 * <pre>
	 *   
	 * |  1      xShear  0 |
	 * |  yShear      1  0 |
	 * |  0           0  1 |
	 * </pre>
	 * 
	 * </blockquote> Note that a shear of (1, 1) is <i>not</i> equal to
	 * shear(1, 0) composed with shear(0, 1). Instead, shear(1, 1) corresponds
	 * to a mapping onto the line x = y.
	 * 
	 * @param xShear
	 *            the x component to shear by
	 * @param yShear
	 *            the y component to shear by
	 * @return this transformation, with an updated matrix
	 */
	public AffineTransformation setToShear(double xShear, double yShear) {
		m00 = 1.0;
		m01 = xShear;
		m02 = 0.0;
		m10 = yShear;
		m11 = 1.0;
		m12 = 0.0;
		return this;
	}

	/**
	 * Sets this transformation to be a translation. For a translation by the
	 * vector (x, y) the transformation matrix has the value: <blockquote>
	 * 
	 * <pre>
	 *   
	 * |  1  0  dx |
	 * |  1  0  dy |
	 * |  0  0   1 |
	 * </pre>
	 * 
	 * </blockquote>
	 * 
	 * @param dx
	 *            the x component to translate by
	 * @param dy
	 *            the y component to translate by
	 * @return this transformation, with an updated matrix
	 */
	public AffineTransformation setToTranslation(double dx, double dy) {
		m00 = 1.0;
		m01 = 0.0;
		m02 = dx;
		m10 = 0.0;
		m11 = 1.0;
		m12 = dy;
		return this;
	}

	/**
	 * Updates the value of this transformation to that of a reflection
	 * transformation composed with the current value.
	 * 
	 * @param x0
	 *            the x-ordinate of a point on the line to reflect around
	 * @param y0
	 *            the y-ordinate of a point on the line to reflect around
	 * @param x1
	 *            the x-ordinate of a point on the line to reflect around
	 * @param y1
	 *            the y-ordinate of a point on the line to reflect around
	 * @return this transformation, with an updated matrix
	 */
	public AffineTransformation reflect(double x0, double y0, double x1,
			double y1) {
		compose(reflectionInstance(x0, y0, x1, y1));
		return this;
	}

	/**
	 * Updates the value of this transformation to that of a reflection
	 * transformation composed with the current value.
	 * 
	 * @param x
	 *            the x-ordinate of the line to reflect around
	 * @param y
	 *            the y-ordinate of the line to reflect around
	 * @return this transformation, with an updated matrix
	 */
	public AffineTransformation reflect(double x, double y) {
		compose(reflectionInstance(x, y));
		return this;
	}

	/**
	 * Updates the value of this transformation to that of a rotation
	 * transformation composed with the current value.
	 * 
	 * @param theta
	 *            the angle to rotate by
	 * @return this transformation, with an updated matrix
	 */
	public AffineTransformation rotate(double theta) {
		compose(rotationInstance(theta));
		return this;
	}

	/**
	 * Updates the value of this transformation to that of a rotation
	 * transformation composed with the current value.
	 * 
	 * @param sinTheta
	 *            the sine of the angle to rotate by
	 * @param cosTheta
	 *            the cosine of the angle to rotate by
	 * @return this transformation, with an updated matrix
	 */
	public AffineTransformation rotate(double sinTheta, double cosTheta) {
		compose(rotationInstance(sinTheta, cosTheta));
		return this;
	}

	/**
	 * Updates the value of this transformation to that of a scale
	 * transformation composed with the current value.
	 * 
	 * @param xScale
	 *            the value to scale by in the x direction
	 * @param yScale
	 *            the value to scale by in the y direction
	 * @return this transformation, with an updated matrix
	 */
	public AffineTransformation scale(double xScale, double yScale) {
		compose(scaleInstance(xScale, yScale));
		return this;
	}

	/**
	 * Updates the value of this transformation to that of a shear
	 * transformation composed with the current value.
	 * 
	 * @param xShear
	 *            the value to shear by in the x direction
	 * @param yShear
	 *            the value to shear by in the y direction
	 * @return this transformation, with an updated matrix
	 */
	public AffineTransformation shear(double xShear, double yShear) {
		compose(shearInstance(xShear, yShear));
		return this;
	}

	/**
	 * Updates the value of this transformation to that of a translation
	 * transformation composed with the current value.
	 * 
	 * @param x
	 *            the value to translate by in the x direction
	 * @param y
	 *            the value to translate by in the y direction
	 * @return this transformation, with an updated matrix
	 */
	public AffineTransformation translate(double x, double y) {
		compose(translationInstance(x, y));
		return this;
	}

	/**
	 * Composes the given {@link AffineTransformation} with this transformation.
	 * This produces a transformation whose effect is equal to applying this
	 * transformation followed by the argument transformation. Mathematically,
	 * <blockquote>
	 * 
	 * <pre>
	 * A.compose(B) = T&lt;sub&gt;B&lt;/sub&gt; x T&lt;sub&gt;A&lt;/sub&gt;
	 * </pre>
	 * 
	 * </blockquote>
	 * 
	 * @param trans
	 *            an affine transformation
	 * @return this transformation, with an updated matrix
	 */
	public AffineTransformation compose(AffineTransformation trans) {
		double mp00 = trans.m00 * m00 + trans.m01 * m10;
		double mp01 = trans.m00 * m01 + trans.m01 * m11;
		double mp02 = trans.m00 * m02 + trans.m01 * m12 + trans.m02;
		double mp10 = trans.m10 * m00 + trans.m11 * m10;
		double mp11 = trans.m10 * m01 + trans.m11 * m11;
		double mp12 = trans.m10 * m02 + trans.m11 * m12 + trans.m12;
		m00 = mp00;
		m01 = mp01;
		m02 = mp02;
		m10 = mp10;
		m11 = mp11;
		m12 = mp12;
		return this;
	}

	/**
	 * Composes this transformation with the given {@link AffineTransformation}.
	 * This produces a transformation whose effect is equal to applying the
	 * argument transformation followed by this transformation. Mathematically,
	 * <blockquote>
	 * 
	 * <pre>
	 * A.composeBefore(B) = T&lt;sub&gt;A&lt;/sub&gt; x T&lt;sub&gt;B&lt;/sub&gt;
	 * </pre>
	 * 
	 * </blockquote>
	 * 
	 * @param trans
	 *            an affine transformation
	 * @return this transformation, with an updated matrix
	 */
	public AffineTransformation composeBefore(AffineTransformation trans) {
		double mp00 = m00 * trans.m00 + m01 * trans.m10;
		double mp01 = m00 * trans.m01 + m01 * trans.m11;
		double mp02 = m00 * trans.m02 + m01 * trans.m12 + m02;
		double mp10 = m10 * trans.m00 + m11 * trans.m10;
		double mp11 = m10 * trans.m01 + m11 * trans.m11;
		double mp12 = m10 * trans.m02 + m11 * trans.m12 + m12;
		m00 = mp00;
		m01 = mp01;
		m02 = mp02;
		m10 = mp10;
		m11 = mp11;
		m12 = mp12;
		return this;
	}

	public Coordinate transform(Coordinate src, Coordinate dest) {
		double xp = m00 * src.x + m01 * src.y + m02;
		double yp = m10 * src.x + m11 * src.y + m12;
		dest.x = xp;
		dest.y = yp;
		return dest;
	}

	public CoordinateSequence transform(CoordinateSequence seq) {
		for (int i = 0; i < seq.size(); i++) {
			double xp = m00 * seq.getOrdinate(i, 0) + m01
					* seq.getOrdinate(i, 1) + m02;
			double yp = m10 * seq.getOrdinate(i, 0) + m11
					* seq.getOrdinate(i, 1) + m12;
			seq.setOrdinate(i, 0, xp);
			seq.setOrdinate(i, 1, yp);
		}
		return seq;
	}

	public void filter(Coordinate pt) {
		double xp = m00 * pt.x + m01 * pt.y + m02;
		double yp = m10 * pt.x + m11 * pt.y + m12;
		pt.x = xp;
		pt.y = yp;
	}

	/**
	 * Tests if this transformation is the identity transformation.
	 * 
	 * @return true if this is the identity transformation
	 */
	public boolean isIdentity() {
		return (m00 == 1 && m01 == 0 && m02 == 0 && m10 == 0 && m11 == 1 && m12 == 0);
	}

	/**
	 * Tests if an object is an <tt>AffineTransformation</tt> and has the same
	 * matrix as this transformation.
	 * 
	 * @param obj
	 *            an object to test
	 * @return true if the given object is equal to this object
	 */
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		AffineTransformation trans = (AffineTransformation) obj;
		return m00 == trans.m00 && m01 == trans.m01 && m02 == trans.m02
				&& m10 == trans.m10 && m11 == trans.m11 && m12 == trans.m12;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		long temp;
		temp = Double.doubleToLongBits(m00);
		result = prime * result + (int) (temp ^ (temp >>> 32));
		temp = Double.doubleToLongBits(m01);
		result = prime * result + (int) (temp ^ (temp >>> 32));
		temp = Double.doubleToLongBits(m02);
		result = prime * result + (int) (temp ^ (temp >>> 32));
		temp = Double.doubleToLongBits(m10);
		result = prime * result + (int) (temp ^ (temp >>> 32));
		temp = Double.doubleToLongBits(m11);
		result = prime * result + (int) (temp ^ (temp >>> 32));
		temp = Double.doubleToLongBits(m12);
		result = prime * result + (int) (temp ^ (temp >>> 32));
		return result;
	}

	/**
	 * Gets a text representation of this transformation. The string is of the
	 * form:
	 * 
	 * <pre>
	 * AffineTransformation[[m00, m01, m02], [m10, m11, m12]]
	 * </pre>
	 * 
	 * @return a string representing this transformation
	 * 
	 */
	public String toString() {
		return "AffineTransformation[[" + m00 + ", " + m01 + ", " + m02
				+ "], [" + m10 + ", " + m11 + ", " + m12 + "]]";
	}

	/**
	 * Clones this transformation
	 * 
	 * @return a copy of this transformation
	 */
	public Object clone() {
		return new AffineTransformation(this);
	}
}
